function clone(obj){
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = obj.constructor(); // changed

    for(var key in obj)
        temp[key] = clone(obj[key]);
    return temp;
}

function Map(){
    var _map = {}, _empty = true;
    this.put = function(key, value) {
        if(_empty)_empty = false;
        _map[key] = value;
    };
    this.get = function(key, cloned) {
        return (cloned)?clone(_map[key]):_map[key];
    };
    this.has = function(key) {
        return _map.hasOwnProperty(key);
    };
    this.keys = function() {
        var keys = [];
        if(!_empty) {
            for (var key in _map) {
                if(this.has(key)) {
                    keys.push(key);
                }
            }
        }
        return keys;
    };
    this.values = function(filter) {
        var values = [], keys, i, value;
        if(!_empty) {
            keys = this.keys();
            for (i = 0; i < keys.length; i++) {
                value = _map[keys[i]];
                if(filter && (typeof filter == "function")) {
                    if(filter.call(value)){
                        values.push(value);
                    }
                } else {
                    values.push(value);
                }
            }
        }
        return values;
    };
    this.remove = function(key) {
       delete _map[key];
    };
    this.isEmpty = function() {
        return _empty;
    };
    this.empty = function() {
        _map = null;
        _empty = true;
        _map = {};
    };
}

//Leaves.prototype = Map

function Leaves() {
   var STATUS = {
       NEW:"new",
       DELETED:"deleted",
       SAVED:"saved"
   }, _leaveObj = {
       id:null,
       date:null,
       state:STATUS.NEW // new || deleted || saved
   }, _map = new Map(), 
   _genearteId =  function() {
        var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz",
            string_length = 8, randomstring = '';
        for (var i=0; i<string_length; i++) {
            var rnum = Math.floor(Math.random() * chars.length);
            randomstring += chars.substring(rnum,rnum+1);
        }
        return randomstring;
    };

    this.loadLeaves = function(_leaves) {
        for (var i = 0; i < _leaves.length; i++) {
            var _leave = _leaves[i];
            _map.put(_leave.id, _leave);
        }
    };

    this.addLeave = function(leaveId, date) {
        if(leaveId==null || !_map.has(leaveId)) { // Means New
            var newLeave = clone(_leaveObj), _id = _genearteId();
            newLeave.id = _id;
            newLeave.date = date;
            _map.put(_id, newLeave);
            delete newLeave;
            return _id;
        } else {            
            var leave = _map.get(leaveId);
            leave.state = STATUS.SAVED;
            return leaveId;
        }        
    };

    this.deleteLeave = function(leaveId) {
        if(_map.has(leaveId)) {
            var leave = _map.get(leaveId);
            if(leave.state==STATUS.NEW) {
                _map.remove(leaveId);
            } else {
                leave.state = STATUS.DELETED;
            }
        }
    };
    
    this.getDeletedLeaves = function() {
        if(_map.isEmpty()) return [];
        return _map.values(function(){
            return this.state==STATUS.DELETED;
        });

    };
    
    this.getNewLeaves = function() {
        if(_map.isEmpty()) return [];
        return _map.values(function(){
            return this.state==STATUS.NEW;
        });
    };

    this.getAllChanges = function() {
        if(_map.isEmpty()) return [];
        return _map.values(function(){
            return (this.state==STATUS.NEW || this.state==STATUS.DELETED);
        });
    };
    
    this.getAll = function(filter) {
        if(_map.isEmpty()) return [];
        return _map.values(filter);
    };

    this.setAllAsSaved = function() {
        if(_map.isEmpty()) return;
        var _changes = _map.values(function(){
            return (this.state==STATUS.NEW || this.state==STATUS.DELETED);
        });
        for (var i = 0; i < _changes.length; i++) {
            var leave = _changes[i];
            leave.state = STATUS.SAVED;
        }        
    };

    this.rollbackChanges = function() {
        if(_map.isEmpty()) return;
        var _changes = _map.values(function(){
            return (this.state==STATUS.NEW || this.state==STATUS.DELETED);
        });
        for (var i = 0; i < _changes.length; i++) {
            var leave = _changes[i];
            if(leave.state==STATUS.NEW) {
                _map.remove(leave.id);
            } else {
                leave.state = STATUS.SAVED;
            }
        }
    };   
}

function FCController() {
    this._calInstance = null;
    this._selectdDatesMap = new Leaves();
    this.selectionId = "fcc-sel-key";
    var opts = {
        selClass: "ui-state-highlight"
    };
    this._getOpts = function(opt) {
        return opts.hasOwnProperty(opt)?opts[opt]:null;
    };
}

FCController.prototype = {
    render: function(calInstance) {
        if(!calInstance) return;
        this._calInstance = calInstance;        
    },
    onDayClick: function (date, allDay, jsEvent, view) {
        if((date>view.start && date<view.end) || date-view.start==0){
            var $cell = this._getCellFromDate(date, view);
            if($cell.hasClass(this._getOpts("selClass"))) {
                this._unSelectDate($cell);
            } else {
                this._selectDate(date, $cell);
            }
            $cell.toggleClass(this._getOpts("selClass"));
        }
    },
    onViewChange: function(view) {
        this._getSelectedCells(view).removeClass(this._getOpts("selClass"));
        this._renderSelected(view);
        
    },
    getDeletedLeaves: function() {
        return this._selectdDatesMap.getDeletedLeaves();
    },
    getNewLeaves: function() {
        return this._selectdDatesMap.getNewLeaves();
    },
    getAllChanges: function() {
        return this._selectdDatesMap.getAllChanges();
    },
    setAllAsSaved: function() {
        return this._selectdDatesMap.setAllAsSaved();
    },
    rollbackChanges: function() {
        return this._selectdDatesMap.rollbackChanges();
    },
    loadLeaves: function(leaves) {
        var _normalizedLeaves = [];
        if(!$.isArray(leaves))  throw "Not an array";
        $(leaves).each(function(ind,_leave){
        	var _date = _leave["date"];
        	if(_date) {
        		var _dateParts = _date.split("/"),
        		_month = parseInt(_dateParts[0], 10)-1;
        		_date = new Date(_dateParts[2],_month,_dateParts[1]);
        	} else {
        		_date = null;
        	}
            var temp =  {
               id: _leave["id"],
               date:_date,
               state:_leave["state"] || STATUS.SAVED // new || deleted || saved
           };
           _normalizedLeaves.push(temp);
        });
        this._selectdDatesMap.loadLeaves(_normalizedLeaves);
        this.onViewChange(this._getView());
    },    
    _getView: function(){
        return this._calInstance.fullCalendar('getView');
    },
    _getSelectedCells: function(view) {
        return $("tbody tr td."+this._getOpts("selClass"),view.element);
    },
    _getDateFromCell: function(td, view){
        var cellPos = {
            row: td.parents('tbody').children().index(td.parent()),
            col: td.parent().children().index(td)
        }, _view = view || this._getView();
        return _view.cellDate(cellPos);
    },
    _getCellFromDate: function(dates, view){
        var _dates = [],_cells = [];
        if($.isArray(dates)) {
            _dates = dates;
        } else {
            _dates.push(dates);
        }
        
        $(_dates).each(function(ind,date){
            var _view = view || this._getView(),
            coords = _view.dateCell(date),
            $row = $("tbody tr",view.element).eq(coords.row),
            $cell = $row.find('td').eq(coords.col);
            _cells.push($cell);
        });
        
        return ($.isArray(dates))? _cells : _cells[0];
    },
    _selectDate: function(date, $cell) {
        var leaveId = $cell.data(this.selectionId),
         result = this._selectdDatesMap.addLeave(leaveId, date);
        if(!leaveId) {
            $cell.data(this.selectionId, result);
        }
    },
    _unSelectDate: function($cell) {
        var leaveId = $cell.data(this.selectionId);
        if(!leaveId) return;
        this._selectdDatesMap.deleteLeave(leaveId);
    },
    _renderSelected: function(view) {
        var _view = view || this._getView(), _dates=[], cells, _cls = this._getOpts("selClass"),
        _thisMonthLeaves = this._selectdDatesMap.getAll(function(){
                    var date = this.date;
                    return ((date>_view.start && date<_view.end) || date-_view.start==0);
                });
        if(!_thisMonthLeaves.length) return;
        $(_thisMonthLeaves).each(function(ind,leave){
            _dates.push(leave.date);
        });
        if(!_dates.length) return;
        cells = this._getCellFromDate(_dates,_view);
        $(cells).each(function(ind,$cell){
            $cell.addClass(_cls);
        });
    }    
};


var fccInst = null;

$(document).ready(function() {

    fccInst = new FCController();
    
    var calendar = $('#calendar').fullCalendar({
        theme: true,
        header: {
            right: 'prev,next today',
            left: 'title'
           // right: 'month,agendaWeek,agendaDay'
        },
        dayClick: function(date, allDay, jsEvent, view) {
            fccInst.onDayClick(date, allDay, jsEvent, view);
        },
        viewDisplay: function(view) {
            fccInst.onViewChange(view);
        },
        events:getEvents()
    });

    fccInst.render(calendar);
    
    $.post(
    		"ttgetDataServlet",
    		{action: 'leaves'},
    		function(data) {
    			fccInst.loadLeaves(data);
    		}
    );
    
    $("#saveCalendar").click(function(){
        var allChanges = fccInst.getAllChanges(), selectedDates = [], selectedTypes = [];
        for ( var xx = 0; xx < allChanges.length; xx++) {
			var array_element = allChanges[xx];
			 selectedDates.push($.fullCalendar.formatDate(array_element.date, "MM/dd/yyyy"));
			 selectedTypes.push(array_element.state);
		}
        if (selectedDates.length > 0) {
        	var dates = selectedDates.join(","),
        	types = selectedTypes.join(","); 
            $.post(
        	    "ttaddHoliday",
        	    { leaves: dates, types: types, type : "0" },
        	    function(data){
        	    	notifierInst.showMessage(data);
        	    	fccInst.setAllAsSaved();
        	    }
            );
        }
    });
});

function getEvents() {
   //return [];
    var _normalizedLeaves = [];
    $.post(
    		"ttgetDataServlet",
    		{action: 'dataForH'},
    		function(leaves) {
    			if(!$.isArray(leaves))  throw "Not an array";
    		    $(leaves).each(function(ind,_leave){
    		    	var _date = _leave["start"];
    		    	if(_date) {
    		    		var _dateParts = _date.split("/"),
    		    		_month = parseInt(_dateParts[0], 10)-1;
    		    		_date = new Date(_dateParts[2],_month,_dateParts[1]);
    		    	} else {
    		    		_date = null;
    		    	}
    		        var temp =  {
    		           title: _leave["title"],
    		           start:_date
    		       };
    		       _normalizedLeaves.push(temp);
    		    });
    		}
    );
    
    return _normalizedLeaves;
}